﻿/**
 * Color
 * 
 * @author		Allen Chou
 * @version		1.0.0 (last update: Mar 29 2009)
 * @link        http://cjcat.blogspot.com
 * @link		http://cjcat2266.deviantart.com
 */

package idv.cjcat.display {
	import idv.cjcat.*;
	
	/**
	 * 24-bit color.
	 */
	public class Color {
		
		private var _rgb:uint = 0x000000;
		
		public function Color(rgb:uint = 0x000000) {
			setColor(rgb);
		}
		
		/**
		 * Sets the color by a 24-bit RGB value;
		 * @param	rgb
		 */
		public function setColor(rgb:uint):void {
			_rgb = CJMath.clamp(rgb, 0x000000, 0xFFFFFF);
			dirtyMinMax = true;
		}
		
		/**
		 * Sets the color by separated RGB components ranging from 0 to 255.
		 * @param	r
		 * @param	g
		 * @param	b
		 */
		public function setRGB(r:uint, g:uint, b:uint):void {
			this.r = r;
			this.g = g;
			this.b = b;
		}
		
		/**
		 * Sets the color by HSV components, H ranging from 0 to 360, S ranging from 0 to 1, V ranging from 0 to 1.
		 * @param	h
		 * @param	s
		 * @param	v
		 */
		public function setHSV(h:Number, s:Number, v:Number):void {
			hsvToRGB(h, s, v);
		}
		
		public function get r():uint { return (_rgb & 0xFF0000) >> 16; }
		public function set r(value:uint):void {
			_rgb &= 0x00FFFF;
			_rgb |= (CJMath.clamp(value, 0x00, 0xFF) << 16);
			dirtyMinMax = true;
		}
		
		public function get g():uint { return (_rgb & 0x00FF00) >> 8; }
		public function set g(value:uint):void {
			_rgb &= 0xFF00FF;
			_rgb |= (CJMath.clamp(value, 0x00, 0xFF) << 8);
			dirtyMinMax = true;
		}
		
		public function get b():uint { return (_rgb & 0x0000FF); }
		public function set b(value:uint):void {
			_rgb &= 0xFFFF00;
			_rgb |= CJMath.clamp(value, 0x00, 0xFF);
			dirtyMinMax = true;
		}
		
		public function get h():Number {
			if ((r == g) && (g == b)) return 0;
			
			if (dirtyMinMax) setMinMax();
			
			var delta:Number = max - min;
			switch (max) {
				case fr:
					return (60 * (fg - fb) / delta + 360) % 360;
					break;
				case fg:
					return (60 * (fb - fr) / delta) + 120;
					break;
				case fb:
					trace(fb);
					return (60 * (fr - fg) / delta) + 240;
					break;
			}
			return 0;
		}
		public function set h(value:Number):void {
			hsvToRGB(value, s, v);
		}
		
		public function get s():Number {
			if ((r == g) && (g == b)) return 0;
			
			if (dirtyMinMax) setMinMax();
			
			if (max == 0) return 0;
			else return delta / max;
		}
		public function set s(value:Number):void {
			hsvToRGB(h, value, v);
		}
		
		public function get v():Number {
			if (dirtyMinMax) setMinMax();
			return max;
		}
		public function set v(value:Number):void {
			hsvToRGB(h, s, value);
		}
		
		public function get brightness():Number {
			return (r * 0.3086 + g * 0.6094 + b * 0.0820) * 0.00390625;
		}
		
		private var dirtyMinMax:Boolean;
		private var fr:Number, fg:Number, fb:Number
		private var min:Number, max:Number, delta:Number;
		private function setMinMax():void {
			//divide by 256
			fr = r * 0.00390625;
			fg = g * 0.00390625;
			fb = b * 0.00390625;
			
			min = fr;
			if (fg < min) min = fg;
			if (fb < min) min = fb;
			max = fr;
			if (fg > max) max = fg;
			if (fb > max) max = fb;
			
			delta = max - min;
			
			dirtyMinMax = false;
		}
		
		private function hsvToRGB(h:Number, s:Number, v:Number):void {
			s = CJMath.clamp(s, 0, 1);
			v = CJMath.clamp(v, 0, 1);
			
			var hi:uint = uint(h / 60) % 6;
			var f:Number = (h / 60) - hi;
			var p:Number = v * (1 - s);
			var q:Number = v * (1 - f * s);
			var t:Number = v * (1 - (1 - f) * s);
			
			switch (hi) {
				case 0:
					r = uint(v * 256);
					g = uint(t * 256);
					b = uint(p * 256);
					break;
				case 1:
					r = uint(q * 256);
					g = uint(v * 256);
					b = uint(p * 256);
					break;
				case 2:
					r = uint(p * 256);
					g = uint(v * 256);
					b = uint(t * 256);
					break;
				case 3:
					r = uint(p * 256);
					g = uint(q * 256);
					b = uint(v * 256);
					break;
				case 4:
					r = uint(t * 256);
					g = uint(p * 256);
					b = uint(v * 256);
					break;
				case 5:
					r = uint(v * 256);
					g = uint(p * 256);
					b = uint(q * 256);
					break;
			}
			dirtyMinMax = true;
		}
		
		public function toString():String {
			var str:String = _rgb.toString(16).toUpperCase();
			if (str.length == 5) str = "0" + str;
			return "0x" + str;
		}
		public function get value():uint { return _rgb; }
	}
}